#!/usr/bin/env python
'''
        AlignedSent - 
        This class is responsible for storing and handling the translation 
        alignments between two sentences 
		
		Baolin Jing, Kai Chen

'''


import re


class AlignedSent(object):
    '''
        This class is responsible for storing and handling the 
        translation alignments between two sentences 
   '''

    # Below are explanations of instance variables in this class.

    #    self.t1 = []      # A list of words in the source sentence
    #    self.t2 = []      # A list of words in the target sentence

    #    These two variables contains mappings from one language to another.
    #    For example, if t1 contains English words, then t1align is a list that
    #    contains two items (pos1, confidence) that represent the alignment to the
    #    French sentence. In the tuple, pos1 is the position of the corresponding
    #    word in the French sentence, and confidence is the confidence value.
    #    The position of the word in the English sentence is represented by the
    #    index value.

    #    self.t1align = []       # t1align denotes mappings to t2
    #    self.t2align = []       # t2align denotes mappings to t1
                                            # These mappings should be [ [ position, confidence ], ... ]
                                            # If no confidence is set for a given word, assumed = 1

    #    self.lang1 = ""
    #    self.lang2 = ""
    #	 

    #    self.alignment_type = 'S'
                     # Should be one of 'S' (sure) or 'P' (probable)
                     # Defaults to 'S'


    def __init__(self, t1=[], t2=[], alignments=[], aligntype='S'):
        '''
            Initialiser for AlignedSent

            Note that the default accepted 'aligntype' is set to SURE.
            This means the class will only accept 'SURE' alignments and throw away PROBABLE alignments

            Override this with 'P' if you wish to retain only PROBABLE alignments and throw away SURE alignments
                @type  t1:         string
                @param t1:         original sentence
                @type  t2:         string
                @param t2:         translated target sentence
                @type  alignments: string
                @param alignments: alignment mapping
                @type  aligntype:  char
                @param aligntype:  alignment type

        '''

        # can set these as user specifies. 
        self.lang1 = 'English'
        self.lang2 = 'French'

        self.alignment_type = aligntype
        
        # Populate t1align and t2align
        # t1align should be a forward mapping t1 -> t2. 
        # t2align should be a reverse mapping t2 -> t1.

        self.t1 = []    # Default value of t1: empty list
        if (t1 != []):
            # Split based on word boundaries - need to ensure this matches 
            # our alignments
            #
            # Assume strings are delimited only by a single 'space' character
            self.t1 = re.split (r' ', t1)
            
        self.t2 = []    # Default value of t2: empty list
        if (t2 != []):
            # Split based on word boundaries - need to ensure this matches 
            # our alignments
            #
            # Assume strings are delimited only by a single 'space' character
            self.t2 = re.split (r' ', t2)

        if (alignments != []):
            #
            #    [('1', '1', 'P', ''), ('1', '2', 'P', ''), ('2', '3', 'P', '')
            #
            #    Alignments are passed in as above

            #  Populate the alignment vectors for t1align
            #  as   [[(1, 1, 1)], [(2, 2, 1)], [(3, 0, 0)], [(4, 0, 0)], [(5, 5, 1)], [(6, 6, 1)]]

            self.t1align = [ [] for x in range(0, len(self.t1))]
            self.t2align = [ [] for x in range(0, len(self.t2))]

            for (source_pos, target_pos, aligntype, confidence) in alignments:

                # Throw away alignments not matching alignment_type
                if (aligntype != self.alignment_type):
                    continue

                if (confidence==''):
                    confidence = 1

                self.t1align[int(source_pos)-1].append (( (int(source_pos)),  int(target_pos),  \
                                                      confidence    ))
                

            # Populate the reverse lookup alignment vector - t2align

            self.t2align = self.__reverse_lookup (self.t1align, len(self.t2))


    def alignments(self):
        alignments = []
        for source_word in self.t1align:
            for target_word in source_word:
                    alignments.append((target_word[0], target_word[1]))

        return alignments


    def __reverse_lookup(self, alignments, targetlen):
        '''
            This private function creates a reverse lookup of the given
            alignment set.

            For example, an alignment represents the alignments positions 
            from t1 to t2.

            This function will return the reverse of this, the alignments
            from t2 to t1.

                @type  alignments: list
                @param alignments: the alignemtns mapping from t1 to t2
                @type  targetlen:  number
                @param targetlen:  length of the alignments list
                @return:           a list of alignment from t2 to t1
        '''

        # Initialise the alignment list

        reverse = [ [] for x in range(0, targetlen)]


        # Count each position as we traverse the original alignment list
        pos = 1

        for alignment in alignments:
            # For each set of alignments  (Yes, we can have more than 
            # one alignment for a given position)

            # as   [[(1, 1)], [(2, 1)], [(0, 0)], [(0, 0)], [(5, 1)], [(6, 1)]]

            for (source_pos, position, confidence) in alignment:
                # Process each of them in turn

    
                # A position of '0' denotes there is no alignment

                if (position != 0):
                    reverse[position-1].append ( (pos, confidence) )

            pos = pos+1
    
        return reverse


    def nth_lookup(self, lang, position):
        '''
            Given a word's position, get the positon of the corresponding word in the
            other language
     
                @type  lang:     string
                @param lang:     the langauge from which we pick a word and look up the word in the other language that's aligned to it.
                @type  position: number
                @param position: the position where the source word is at
                @return:         the position where the aligned word is at. Value -1 means the given argument is in invalid range. Value -2 means the specified language is not valid
        '''

        if lang == self.lang1:
            if position > len(self.t1) or position < 1:
                return -1
            else:
                return self.t1align[position - 1]
        elif lang == self.lang2:
            if position > len(self.t2) or position < 1:
                return -1
            else:
                return self.t2align[position - 1]
        else:
            return -2


    def get_word(self, lang, position):
        '''
            Get word from a specified language from specified position

            @type  lang:     string
            @param lang:     language in which we want to get word
            @type  position: number
            @param position: the position where the desired word is at
            @return:         the word from the specified position and language. value -1 means position is out of range. value -2 means specified language is invalid
        '''

        if lang == self.lang1:
            if position > len(self.t1) or position < 1:
                return -1
            else:
                return self.t1[position - 1]
        elif lang == self.lang2:
            if position > len(self.t2) or position < 1:
                return -1
            else:
                return self.t2[position - 1]
        else:
            return -2

    def whole_sentence(self, lang):
        '''
            This method returns a whole sentence in one language given the
            language

                @type  lang:     string
                @param lang:     language in which we want the whole sentence
                @return:         a string containing the whole sentence
                                 value-1 means language specified is not valid 
        '''
        if lang == self.lang1:
            return ' '.join(self.t1)
        elif lang == self.lang2:
            return ' '.join(self.t2)
        else:
            return -1

